#include <random>
#include <cmath>
#include <vector>
#include <iostream>
#include <string>

class PriceGenerator {
public:
    PriceGenerator(double lastPrice, double standardDeviation) {
        using namespace std;
        random_device rd;
        e.seed(rd());
        u = normal_distribution<double>(0, standardDeviation);
        this->lastPrice = lastPrice;
    }
    double getNextPrice() {
        return getNextPrice(lastPrice);
    }
    double getNextPrice(double lastPrice) {
        auto newPrice = std::round(std::exp(u(e)) * lastPrice * 100) / 100;
        this->lastPrice = newPrice;
        return newPrice;
    }
private:
    std::default_random_engine e;
    std::normal_distribution<double> u;
    double lastPrice;
};

class OneDaySimulator {
public:
    OneDaySimulator(PriceGenerator& generator) {
        init(generator.getNextPrice(), generator, 10);
    }
    OneDaySimulator(PriceGenerator& generator, const OneDaySimulator& lastDaySimulator) {
        init(lastDaySimulator.getClose(), generator, 10);
    }
    double getMax() const {
        double max = prices.back();
        for (const auto& x : prices) {
            if (x > max) {
                max = x;
            }
        }
        return max;
    }
    double getMin() const {
        double min = prices.back();
        for (const auto& x : prices) {
            if (x < min) {
                min = x;
            }
        }
        return min;
    }
    double getOpen() const {
        return open;
    }
    double getClose() const {
        return prices.back();
    }
private:
    std::vector<double> prices;
    double open;
    double roundForPrice(double x) {
        return std::round(100 * x) / 100;
    }
    void init(double lastDayClose, PriceGenerator& generator, int maxTrade) {
        double maxPrice = roundForPrice(lastDayClose * 1.1);
        double minPrice = roundForPrice(lastDayClose * 0.9);
        bool touchMax = false;
        bool touchMin = false;

        do {
            open = generator.getNextPrice(lastDayClose);
        } while (open <= minPrice || open >= maxPrice);
        prices.emplace_back(open);

        double price;
        for (int i = 1; i < maxTrade; i++) {
            do {
                price = generator.getNextPrice(prices.back());
            } while ((touchMax || touchMin) && (price <= minPrice || price >= maxPrice));
            if (price > maxPrice) {
                touchMax = true;
                price = maxPrice;
            }
            if (price < minPrice) {
                touchMin = true;
                price = minPrice;
            }
            prices.emplace_back(price);
        }
    }
};

void printData(const OneDaySimulator& lastDay) {
    using namespace std;
    cout << lastDay.getOpen() << "," << lastDay.getMax() << "," << lastDay.getMin() << "," << lastDay.getClose() << endl;
}

int main(int argc, char* argv[]) {
    using namespace std;
    int rows = 100;
    for (int i = 0; i < argc; i++) {
        if (string(argv[i]) == string("-n") && i + 1 < argc) {
            rows = stoi(string(argv[i + 1]));
        }
    }
    PriceGenerator g(20., .005);
    OneDaySimulator lastDay(g);

    cout << "Open,Max,Min,Close" << endl;
    printData(lastDay);

    for (int i = 0; i < rows - 1; i++) {
        lastDay = OneDaySimulator(g, lastDay);
        printData(lastDay);
    }
    return 0;
}
